Example 1: Azure Function to Calculate Invoice Totals; Runs Hourly to Set Account Loyalty Level
In this example, we are going to create an Azure Function app that runs each hour (for a high transaction business) and updates CRM accounts with a loyalty value. The scenario is that we want to provide premium level service to the accounts that have reached a sales and commitment level for the past six months.
Note: There are ways to automate several of the steps for creating this Azure Function. The steps below show the manual, hands-on way to deploy a Function that works with Dynamics CRM.
Also, Microsoft is constantly changing the Azure Portal and Azure services. As a result, some of the screens and steps you take might differ slightly from the information provided below.
Create the Azure Function app
Login to the Azure Portal
Create a new Azure Function app. Use the TimerTrigger - C# template as the basis for the function, as shown below.
The Timer setting is set to 0 * 1 * * * (a CRON expression) to trigger once every hour. It's also possible to trigger the function on-demand through HTTP (REST or WebHook), from an Azure Logic App, manually running it within the Azure Portal and through other triggering mechanisms.
Add Dynamics CRM SDK NuGet packages to the Function
In order for the Function app to connect to Dynamics CRM, it's necessary to reference the CRM SDK in the Function. One way to provide the CRM SDK DLLs to the Function is to create and upload a project.json file that references CRM NuGet packages.
First, you'll need the FTP host address to get to the Function's folders and files. In the Azure Portal, with your Function in the current blade, click the Function App Settings button and then under Advanced Settings click Go to App Service Settings. Copy the FTP hostname, then open Windows Explorer (or any FTP client app) and navigate to the FTP location.
To obtain the required FTP password, click the Get Publish Profile link in the Azure portal for your Function. Download the publish file, open it, search for "userPWD" and provide the password to the FTP login.
You should now see three folders: data, LogFiles, and site. Click into the site folder, then to wwwroot and then into the folder that's the same name as your Function. You will see an existing function.json file in the folder.
Next, in a text editor, create and upload a file named project.json into the same folder as function.json. Sample contents for project.json is provided below.
Initial Test: Verify that the Azure Function app can connect to Dynamics CRM
Return to your Function source code and copy in the following C# code. Replace the Username and Password settings with your own credentials. (I know, it's bad to put your credentials in plain text. See my comment on this later in this article.)
using System;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Xrm.Client;
using Microsoft.Xrm.Client.Services;
public static void Run(TimerInfo myTimer, TraceWriter log)
{
var connectionString = "AuthType=Office365;Username=timd@contoso.com; Password=abc123;Url=https://contosodev.crm.dynamics.com";
CrmConnection connection = CrmConnection.Parse (connectionString);
using (OrganizationService orgService = new OrganizationService(connection))
{
var query = new QueryExpression("account");
query.ColumnSet.AddColumns("name");
var ec = orgService.RetrieveMultiple(query);
log.Verbose(ec[0].GetAttributeValue<string>("name"));
}
}
Click the Save button. The Logs field should show "Compilation succeeded" as the last entry.
Next, click the Run button. After a few seconds, the Log field should show the name of a CRM account (assuming you have at least one account record).
If you're an experienced Dynamics CRM developer, then the hard part of getting an Azure Function up and running is over! You can write the same type of C# code in Azure Functions that you're used to writing in .NET Console apps, but now you can trigger your code in a lot more ways beyond simply setting up a Windows scheduled task (and making sure the PC or server is running).
A word about storing/using credentials in Azure Functions
Even if you are the only person who has access to the Azure Portal, it's still not a good idea to store your CRM (Office 365) credentials in Azure Functions. One way to get around this is to create a .NET class library that includes the CRM connectivity code in it, along with your encrypted password. Reference that assembly in your Azure Functions to connect to CRM without revealing your credentials.
Loyalty Rollup Calculation Example
Next, we'll look at a simple example for calculating a loyalty ranking for accounts. To keep the example small, the provided code does not include Exception handling or other common data validation. Also, the code doesn't include the code to update the customer records.
using System;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Xrm.Client;
using Microsoft.Xrm.Client.Services;
public static void Run(TimerInfo myTimer, TraceWriter log)
{
var connectionString = "AuthType=Office365;Username=timd@contoso.com; Password=abc123;Url=https://contoso.crm.dynamics.com";
CrmConnection connection = CrmConnection.Parse (connectionString);
using ( OrganizationService orgService = new OrganizationService(connection))
{
// Sum the total of invoices for the past six months where the status is Paid
var fetch = "<fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false' aggregate='true'>";
fetch += " <entity name='invoice'>";
fetch += " <attribute name='customerid' alias='customerid' groupby='true' />";
fetch += " <attribute name='totalamount' alias='total' aggregate='sum' />";
fetch += " <filter type='and'>";
fetch += " <condition value='2' attribute='statecode' operator='eq' />";
fetch += " </filter>";
fetch += " </entity>";
fetch += "</fetch>";
EntityCollection invoiceTotals = orgService.RetrieveMultiple(new FetchExpression(fetch));
foreach (var invEnt in invoiceTotals.Entities)
{
if (((Money)((AliasedValue)invEnt["total"]).Value).Value > 1000000m)
{
// Update the account as a Premium Tier customer (if not already Premium)
log.Verbose("Premium");
}
else
{
// Set the account as a Standard Tier customer (if not already Standard)
log.Verbose("Standard");
}
}
log.Verbose("Process complete.");
}
}
The following screen shows the Function and message indicating successful completion.
Call Azure Functions from a CRM form JavaScript Web Resource, from Workflows or Plug-ins where it fits the use case. For example, call a Function from a plug-in to produce a chart that requires a third-party DLL that can't be incorporated in the plug-in or from a cloud service.
Use an Azure Function to act as a FetchXml tester and reporting tool. Incorporate the use of the ClosedXml library to generate Excel files based on the provided FetchXml statement. Schedule the function to run and email the file to a user.
Monitor CRM metadata changes and send notifications.
Auto-numbering: 1) CRM create event occurs 2) Service Endpoint sends record info to Azure queue 3) Azure Function receives message and gets next number via locked Azure Blob or SQL table 4) Update CRM record with new unique number
Example 1: Azure Function to Calculate Invoice Totals; Runs Hourly to Set Account Loyalty Level
In this example, we are going to create an Azure Function app that runs each hour (for a high transaction business) and updates CRM accounts with a loyalty value. The scenario is that we want to provide premium level service to the accounts that have reached a sales and commitment level for the past six months.
Note: There are ways to automate several of the steps for creating this Azure Function. The steps below show the manual, hands-on way to deploy a Function that works with Dynamics CRM.
Also, Microsoft is constantly changing the Azure Portal and Azure services. As a result, some of the screens and steps you take might differ slightly from the information provided below.
Create the Azure Function app
Login to the Azure PortalCreate a new Azure Function app. Use the TimerTrigger - C# template as the basis for the function, as shown below.
The Timer setting is set to 0 * 1 * * * (a CRON expression) to trigger once every hour. It's also possible to trigger the function on-demand through HTTP (REST or WebHook), from an Azure Logic App, manually running it within the Azure Portal and through other triggering mechanisms.
Add Dynamics CRM SDK NuGet packages to the Function
In order for the Function app to connect to Dynamics CRM, it's necessary to reference the CRM SDK in the Function. One way to provide the CRM SDK DLLs to the Function is to create and upload a project.json file that references CRM NuGet packages.First, you'll need the FTP host address to get to the Function's folders and files. In the Azure Portal, with your Function in the current blade, click the Function App Settings button and then under Advanced Settings click Go to App Service Settings. Copy the FTP hostname, then open Windows Explorer (or any FTP client app) and navigate to the FTP location.
To obtain the required FTP password, click the Get Publish Profile link in the Azure portal for your Function. Download the publish file, open it, search for "userPWD" and provide the password to the FTP login.
You should now see three folders: data, LogFiles, and site. Click into the site folder, then to wwwroot and then into the folder that's the same name as your Function. You will see an existing function.json file in the folder.
Next, in a text editor, create and upload a file named project.json into the same folder as function.json. Sample contents for project.json is provided below.
project.json file contents example:
{ "frameworks": { "net46":{ "dependencies": { "Microsoft.CrmSdk.CoreAssemblies": "7.1.0", "Microsoft.CrmSdk.Deployment": "7.1.0", "Microsoft.CrmSdk.Extensions": "7.1.0" } } } }Initial Test: Verify that the Azure Function app can connect to Dynamics CRM
Return to your Function source code and copy in the following C# code. Replace the Username and Password settings with your own credentials. (I know, it's bad to put your credentials in plain text. See my comment on this later in this article.)
using System; using Microsoft.Xrm.Sdk; using Microsoft.Xrm.Sdk.Query; using Microsoft.Xrm.Client; using Microsoft.Xrm.Client.Services; public static void Run(TimerInfo myTimer, TraceWriter log) { var connectionString = "AuthType=Office365;Username=timd@contoso.com; Password=abc123;Url=https://contosodev.crm.dynamics.com"; CrmConnection connection = CrmConnection.Parse (connectionString); using (OrganizationService orgService = new OrganizationService(connection)) { var query = new QueryExpression("account"); query.ColumnSet.AddColumns("name"); var ec = orgService.RetrieveMultiple(query); log.Verbose(ec[0].GetAttributeValue<string>("name")); } }Click the Save button. The Logs field should show "Compilation succeeded" as the last entry.
Next, click the Run button. After a few seconds, the Log field should show the name of a CRM account (assuming you have at least one account record).
If you're an experienced Dynamics CRM developer, then the hard part of getting an Azure Function up and running is over! You can write the same type of C# code in Azure Functions that you're used to writing in .NET Console apps, but now you can trigger your code in a lot more ways beyond simply setting up a Windows scheduled task (and making sure the PC or server is running).
A word about storing/using credentials in Azure Functions
Even if you are the only person who has access to the Azure Portal, it's still not a good idea to store your CRM (Office 365) credentials in Azure Functions. One way to get around this is to create a .NET class library that includes the CRM connectivity code in it, along with your encrypted password. Reference that assembly in your Azure Functions to connect to CRM without revealing your credentials.
Loyalty Rollup Calculation Example
Next, we'll look at a simple example for calculating a loyalty ranking for accounts. To keep the example small, the provided code does not include Exception handling or other common data validation. Also, the code doesn't include the code to update the customer records.
using System; using Microsoft.Xrm.Sdk; using Microsoft.Xrm.Sdk.Query; using Microsoft.Xrm.Client; using Microsoft.Xrm.Client.Services; public static void Run(TimerInfo myTimer, TraceWriter log) { var connectionString = "AuthType=Office365;Username=timd@contoso.com; Password=abc123;Url=https://contoso.crm.dynamics.com"; CrmConnection connection = CrmConnection.Parse (connectionString); using ( OrganizationService orgService = new OrganizationService(connection)) { // Sum the total of invoices for the past six months where the status is Paid var fetch = "<fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false' aggregate='true'>"; fetch += " <entity name='invoice'>"; fetch += " <attribute name='customerid' alias='customerid' groupby='true' />"; fetch += " <attribute name='totalamount' alias='total' aggregate='sum' />"; fetch += " <filter type='and'>"; fetch += " <condition value='2' attribute='statecode' operator='eq' />"; fetch += " </filter>"; fetch += " </entity>"; fetch += "</fetch>"; EntityCollection invoiceTotals = orgService.RetrieveMultiple(new FetchExpression(fetch)); foreach (var invEnt in invoiceTotals.Entities) { if (((Money)((AliasedValue)invEnt["total"]).Value).Value > 1000000m) { // Update the account as a Premium Tier customer (if not already Premium) log.Verbose("Premium"); } else { // Set the account as a Standard Tier customer (if not already Standard) log.Verbose("Standard"); } } log.Verbose("Process complete."); } }The following screen shows the Function and message indicating successful completion.
References
Azure Functions C# developer referenceIdeas
.